package cn.hcbycloud.gateway.filter;


import cn.hcbycloud.common.core.constant.CommonConstant;
import cn.hcbycloud.common.core.exception.CustomizeException;
import cn.hcbycloud.common.core.service.rest.RestTemplateService;
import cn.hcbycloud.common.jwt.service.encryptionkey.BaseGenerateJwtKeyService;
import cn.hcbycloud.common.jwt.service.jwt.JwtBuilderFactory;
import cn.hcbycloud.common.jwt.service.jwt.JwtRedisCacheService;
import cn.hcbycloud.common.jwt.service.jwt.JwtService;
import cn.hcbycloud.common.jwt.vo.JwtToken;
import cn.hcbycloud.common.jwt.vo.LoginInfo;
import cn.hcbycloud.gateway.service.AbsGatewayInterceptPathService;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @author gaoh
 * @desc 全局登录鉴权过滤器
 * @date 2022年01月11日 上午 12:14
 */
@Slf4j
@SuppressWarnings("all")
@Component
public class GlobalLoginOrRegisterFilter implements GlobalFilter, Ordered {
    /**
     * 注册中心客户端, 可以从注册中心中获取服务实例信息
     */
    private final LoadBalancerClient loadBalancerClient;
    private final RestTemplateService restTemplateService;
    private AbsGatewayInterceptPathService gatewayInterceptPathService;
    private JwtService jwtService;
    private BaseGenerateJwtKeyService generateJwtKeyService;
    private JwtRedisCacheService jwtRedisCacheService;
    private JwtBuilderFactory jwtBuilderService;

    public GlobalLoginOrRegisterFilter(LoadBalancerClient loadBalancerClient,
                                       RestTemplateService restTemplateService,
                                       AbsGatewayInterceptPathService gatewayInterceptPathService,
                                       JwtService jwtService,
                                       BaseGenerateJwtKeyService generateJwtKeyService,
                                       JwtRedisCacheService jwtRedisCacheService,
                                       JwtBuilderFactory jwtBuilderService) {
        this.loadBalancerClient = loadBalancerClient;
        this.restTemplateService = restTemplateService;
        this.gatewayInterceptPathService = gatewayInterceptPathService;
        this.jwtService = jwtService;
        this.generateJwtKeyService = generateJwtKeyService;
        this.jwtRedisCacheService = jwtRedisCacheService;
        this.jwtBuilderService = jwtBuilderService;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        /** 如果是需要鉴权认证的服务，需要从头信息中获取token并进行验证 */
        if (!gatewayInterceptPathService.isIgnoreAuthUrl(request)) {
            HttpHeaders headers = request.getHeaders();
            String token = request.getHeaders().getFirst(CommonConstant.THREAD_LOCAL_TOKEN);
            if (StrUtil.isBlank(token)) {
                throw new CustomizeException("网关层校验:令牌不存在", HttpStatus.SC_UNAUTHORIZED);
            }
            JwtToken jwt = new JwtToken(token);
            try {
                jwtService.verifierToken(token, generateJwtKeyService);
            } catch (ExpiredJwtException e) {
                // 令牌本身已经过期，检测缓存数据库中的token是否过期，如果缓存中存在，说明令牌没有完全过期，进行刷新
                if (jwtRedisCacheService.hasToken(jwt)) {
                    jwtRedisCacheService.removeTokenFromCache(jwt);
                    // 从超时异常信息中获取令牌的payload信息
                    Claims body = e.getClaims();
                    // 转为loginInfo对象
                    LoginInfo loginInfo = JSON.parseObject(body.get(CommonConstant.JWT_LOGIN_INFO_KEY)
                            .toString(), LoginInfo.class);
                    // 重新生成token
                    JwtToken jwtToken = jwtService.generateToken(loginInfo, generateJwtKeyService, jwtBuilderService);
                    // 重新刷新缓存
                    jwtRedisCacheService.setTokenToCache(jwtToken);
                    HttpHeaders httpHeaders = request.getHeaders();
                    ServerHttpRequest.Builder requestBuilder = request.mutate();
                    // 先删除，后新增
                    requestBuilder.header(CommonConstant.THREAD_LOCAL_TOKEN_REFRESHED, jwtToken.getToken());
                    ServerHttpRequest req = requestBuilder.build();
                    exchange.mutate().request(req).build();
                } else {
                    // 令牌已经完全过期，需要重新申请
                    throw new CustomizeException("网关层校验:令牌已过期", HttpStatus.SC_UNAUTHORIZED);
                }
            } catch (SignatureException e) {
                throw new CustomizeException("网关层校验:令牌鉴签错误", HttpStatus.SC_UNAUTHORIZED);
            } catch (Exception e) {
                throw new CustomizeException("网关层校验:无效的令牌", HttpStatus.SC_UNAUTHORIZED);
            }
        }
        // 解析通过, 则放行
        return chain.filter(exchange);
    }

    /*private String getTokenFromAuthorityCenter(ServerHttpRequest request, String uriFormat) {
        // service id 就是服务名字, 负载均衡
        ServiceInstance serviceInstance = loadBalancerClient.choose(GatewayConstant.AUTH_CENTER_SERVICE_ID);
        log.info("Nacos Client Info: [{}], [{}], [{}]",
                serviceInstance.getServiceId(), serviceInstance.getInstanceId(),
                JSON.toJSONString(serviceInstance.getMetadata()));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        String requestUrl = String.format(
                uriFormat, serviceInstance.getHost(), serviceInstance.getPort()
        );

        ResponseEntity<String> resEntity = restTemplateService.post(requestUrl, headers, parseBodyFromRequest(request), String.class, CollUtil.newHashMap());
        String body = resEntity.getBody();
        JSONObject resObj = JSON.parseObject(body);
        String jwtToken = (String) resObj.getJSONObject("data").get("jwtToken");
        if (StrUtil.isNotBlank(jwtToken)) {
            return jwtToken;
        }
        return null;
    }*/

    /**
     * 从Post缓存的请求body中获取到请求数据
     *
     * @param request
     * @return
     */
    /*private String parseBodyFromRequest(ServerHttpRequest request) {
        // 获取请求体
        Flux<DataBuffer> body = request.getBody();
        AtomicReference<String> bodyRef = new AtomicReference<>();
        // 订阅缓冲区去消费请求体中的数据
        body.subscribe(buffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            // 一定要使用 DataBufferUtils.release 释放掉, 否则, 会出现内存泄露
            DataBufferUtils.release(buffer);
            bodyRef.set(charBuffer.toString());
        });
        // 获取 request body
        return bodyRef.get();
    }*/

    /*private String getUriFormat(ServerHttpRequest request) {
        String uriFormat = "http://%s:%s" + request.getURI().getPath();
        if (StrUtil.isNotBlank(request.getURI().getQuery())) {
            uriFormat = uriFormat + "?" + request.getURI().getQuery();
        }
        return uriFormat;
    }*/



    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE + 2;
    }

}